package com.zwh.common.easyexcel;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.converters.AutoConverter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.metadata.property.DateTimeFormatProperty;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.metadata.property.NumberFormatProperty;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
import com.zwh.common.easyexcel.annotation.ExcelFillProperty;


/**
 * @author zwh
 * @date 2021/1/28 10:16
 **/
public class ExcelWriteFillProperty {

    /**
     * Custom class
     */
    private Class headClazz;

    /**
     * Configuration column information
     */
    private Map<String, ExcelContentProperty> fieldNameContentPropertyMap;
    /**
     * Fields ignored
     */
    private Map<String, Field> ignoreMap;

    public ExcelWriteFillProperty(Holder holder, Class headClazz) {
        this.headClazz = headClazz;
        fieldNameContentPropertyMap = new HashMap<String, ExcelContentProperty>();
        ignoreMap = new HashMap<String, Field>(16);
        initColumnProperties(holder);
    }

    private void initColumnProperties(Holder holder) {
        if (headClazz == null) {
            return;
        }
        // Declared fields
        List<Field> defaultFieldList = new ArrayList<Field>();
        Map<String, Field> customFiledMap = new TreeMap<String, Field>();
        FillClassUtils.declaredFields(headClazz, defaultFieldList, customFiledMap, ignoreMap);

        for (Map.Entry<String, Field> entry : customFiledMap.entrySet()) {
            initOneColumnProperty(holder, entry.getKey(), entry.getValue(), Boolean.TRUE);
        }
    }

    /**
     * Initialization column property
     *
     * @param holder
     * @param fieldName
     * @param field
     * @param forceIndex
     * @return Ignore current field
     */
    private boolean initOneColumnProperty(Holder holder, String fieldName, Field field, Boolean forceIndex) {
        if (holder instanceof AbstractWriteHolder) {
            if (((AbstractWriteHolder) holder).ignore(field.getName(), null)) {
                return true;
            }
        }
        ExcelFillProperty excelFillProperty = field.getAnnotation(ExcelFillProperty.class);

        ExcelContentProperty excelContentProperty = new ExcelContentProperty();
        if (excelFillProperty != null) {
            Class<? extends Converter> convertClazz = excelFillProperty.converter();
            if (convertClazz != AutoConverter.class) {
                try {
                    Converter converter = convertClazz.newInstance();
                    excelContentProperty.setConverter(converter);
                } catch (Exception e) {
                    throw new ExcelCommonException("Can not instance custom converter:" + convertClazz.getName());
                }
            }
        }
        excelContentProperty.setField(field);
        excelContentProperty
                .setDateTimeFormatProperty(DateTimeFormatProperty.build(field.getAnnotation(DateTimeFormat.class)));
        excelContentProperty
                .setNumberFormatProperty(NumberFormatProperty.build(field.getAnnotation(NumberFormat.class)));
        fieldNameContentPropertyMap.put(field.getName(), excelContentProperty);
        return false;
    }

    public Class getHeadClazz() {
        return headClazz;
    }

    public void setHeadClazz(Class headClazz) {
        this.headClazz = headClazz;
    }

    public Map<String, ExcelContentProperty> getFieldNameContentPropertyMap() {
        return fieldNameContentPropertyMap;
    }

    public void setFieldNameContentPropertyMap(Map<String, ExcelContentProperty> fieldNameContentPropertyMap) {
        this.fieldNameContentPropertyMap = fieldNameContentPropertyMap;
    }

    public Map<String, Field> getIgnoreMap() {
        return ignoreMap;
    }

    public void setIgnoreMap(Map<String, Field> ignoreMap) {
        this.ignoreMap = ignoreMap;
    }
}
